using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TableTennisHumanPlayerRacket : TableTennisPlayerBase
{
	public Transform racket;

	public Transform fire;

	public bool autoPilot;

	public FloatRange autoSpeed;

	public float pingTime;

	public FloatRange fingerSpeedRange = default(FloatRange);

	protected bool buttonDown;

	protected bool hitBall;

	protected Plane movePlane;

	protected GestureTracker gestureTracker = new GestureTracker();

	protected float time;

	protected GravityBallFlight serveFlight = new GravityBallFlight();

	protected float maxDistanceBehindBallInServe;

	private bool inHitBall;

	public int markerHits;

	public PhysicsBall ball { get; protected set; }

	public TableTennisHumanPlayer.ServeHelper serveHelper { get; protected set; }

	public TTCameraController cam
	{
		get
		{
			return base.match.cameraController;
		}
	}

	public Vector3 highestPositionAfterRebound { get; protected set; }

	protected RacketSettings.Racket racketDef
	{
		get
		{
			return base.match.ui.racketButton.currentRacket;
		}
	}

	public override bool IsInServe()
	{
		return serveHelper.isInServe;
	}

	public override void Init(int teamTag, int tableSide, TableTennisMatchController match, bool canAlwaysHit)
	{
		if (Application.platform != 0)
		{
			autoPilot = false;
		}
		base.Init(teamTag, tableSide, match, canAlwaysHit);
		match.ui.racketButton.gameObject.SetActive(true);
		movePlane = new Plane(Vector3.up, Vector3.up * (Table.tabletopy + 0.2f));
		gestureTracker.MaxQueueSize = 20;
		gestureTracker.minDistanceScreen = 0f;
		gestureTracker.minDistance = 0f;
		fire.gameObject.SetActive(false);
		serveHelper = new TableTennisHumanPlayer.ServeHelper();
		racket.rotation = Quaternion.AngleAxis(-70f, Vector3.forward);
		racket.position = new Vector3(0f, Table.tabletopy + 0.125f, (float)tableSide * Table.halfLength);
		match.ui.cameraButton.onCameraChanged += OnCameraChanged;
		OnCameraChanged();
	}

	private void OnCameraChanged()
	{
		TTCameraController cameraController = base.match.cameraController;
		base.match.cameraController.ChangeGameplayInterval(TTCameraBehaviour.GameplayIntervalType.RacketCamera, TTCameraOptions.ForPlayer(this));
	}

	public override Vector3 playerPos()
	{
		return racket.position;
	}

	private void Update()
	{
		if (InputControl.buttonDown)
		{
			buttonDown = true;
			gestureTracker.Clear();
		}
		if (InputControl.buttonUp)
		{
			buttonDown = false;
			gestureTracker.Clear();
		}
		time += Time.deltaTime;
		if (buttonDown)
		{
			MoveRacketToPos(Input.mousePosition);
		}
		if (!(ball == null))
		{
			if (IsInServe())
			{
				UpdateServeBall();
			}
			CheckIfHitBall();
		}
	}

	private void UpdateServeBall()
	{
		Vector3 position = racket.position;
		float magnitude = CalculateVelocityFromGestureTracker().magnitude;
		float num = Mathf.Lerp(4f, 1f, Mathf.InverseLerp(0f, 2f, magnitude));
		position.x = Mathf.Lerp(ball.position.x, racket.position.x, Time.deltaTime * num);
		position.x = Mathf.Clamp(position.x, 0f - Table.halfwidth, Table.halfwidth);
		position.z = (float)base.tableSide * Table.halfLength;
		position.y = Table.netHeight + Table.tabletopy;
		serveFlight.flightTime += Time.deltaTime;
		serveFlight.flightTime = Mathf.Repeat(serveFlight.flightTime, 2f * serveFlight.apexTime);
		position.y = serveFlight.PositionAt(serveFlight.flightTime).y;
		ball.position = position;
		maxDistanceBehindBallInServe = Mathf.Max((float)base.tableSide * (racket.position.z - position.z), maxDistanceBehindBallInServe);
	}

	private void CheckIfHitBall()
	{
		if (inHitBall || ball == null || (IsInServe() && maxDistanceBehindBallInServe < ball.flightSequence.radius * 1.1f) || (!IsInServe() && ball.flightSequence.flightTime < ball.flightSequence.timeToLand))
		{
			return;
		}
		if (!IsInServe())
		{
			FlightSequence.FlightPlanStage stageThatStartsFromTableSide = ball.flightSequence.GetStageThatStartsFromTableSide(base.tableSide);
			if (stageThatStartsFromTableSide != null)
			{
				float flightTime = stageThatStartsFromTableSide.flight.flightTime;
				Vector3 pos = stageThatStartsFromTableSide.flight.PositionAt(flightTime);
				if (Table.IsPosOnTable(pos) && ball.flightSequence.flightTime > ball.flightSequence.GetTimeToStage(stageThatStartsFromTableSide) + flightTime)
				{
					return;
				}
			}
		}
		LinkedList<FingerPos> queue = gestureTracker.GetQueue();
		Vector3 vector = racket.position;
		Vector3 vector2 = vector;
		float deltaTime = Time.deltaTime;
		if (queue.Count > 0)
		{
			LinkedListNode<FingerPos> last = queue.Last;
			FingerPos value = last.Value;
			FingerPos fingerPos = ((last.Previous == null) ? value : last.Previous.Value);
			deltaTime = value.deltaTime;
			vector2 = fingerPos.pos;
			vector = value.pos;
		}
		Vector3 position = ball.position;
		Vector3 prevPosition = ball.prevPosition;
		float radius = ball.flightSequence.radius;
		float num = 0.5714794f;
		float y = 1.201868f;
		Vector3 vector3 = position - vector;
		float num2 = (float)base.tableSide * vector2.z - (float)base.tableSide * prevPosition.z;
		float num3 = (float)base.tableSide * vector.z - (float)base.tableSide * position.z;
		float num4 = 7f * ball.flightSequence.radius;
		float num5 = -3f * ball.flightSequence.radius;
		float num6 = Vector3.Distance(vector2, vector) / deltaTime / Table.halfwidth;
		float num7 = 0.5f;
		if (num6 < num7)
		{
			num4 = ball.flightSequence.radius;
		}
		if (num2 > num3 && DoTheyIntersect(vector2, vector, new Vector3(1.2f * num * racketDef.paddleWidth, y, 0.6f), prevPosition, position))
		{
			HitBall();
		}
	}

	protected bool DoTheyIntersect(Vector3 prevRacketPos, Vector3 racketPos, Vector3 racketDimensions, Vector3 prevBallPos, Vector3 ballPos)
	{
		float radius = base.match.ball.flightSequence.radius;
		FloatRange floatRange = RangeForPoly(prevRacketPos.x, racketPos.x, racketDimensions.x * 0.5f);
		FloatRange range = RangeForPoly(prevBallPos.x, ballPos.x, radius);
		FloatRange floatRange2 = RangeForPoly(prevRacketPos.z + racketDimensions.z * (float)base.tableSide * 0.5f, racketPos.z + racketDimensions.z * (float)base.tableSide * 0.5f, racketDimensions.z);
		FloatRange range2 = RangeForPoly(prevBallPos.z, ballPos.z, radius);
		return floatRange.IsIntersecting(range) && floatRange2.IsIntersecting(range2);
	}

	private FloatRange RangeForPoly(float prevPos, float pos, float size)
	{
		FloatRange result = default(FloatRange);
		result.min = (result.max = prevPos - size);
		result.min = Mathf.Min(result.min, prevPos + size);
		result.max = Mathf.Max(result.max, prevPos + size);
		result.min = Mathf.Min(result.min, pos - size);
		result.max = Mathf.Max(result.max, pos - size);
		result.min = Mathf.Min(result.min, pos + size);
		result.max = Mathf.Max(result.max, pos + size);
		return result;
	}

	protected IEnumerator DoHitBall()
	{
		inHitBall = true;
		float time = 0f;
		float maxDuration = 0.03f;
		Vector3 startRacketPos = racket.position;
		Vector3 startBallPos = ball.position;
		while (time < maxDuration)
		{
			time += RealTime.deltaTime;
			yield return null;
		}
		HitBall();
		inHitBall = false;
	}

	private void MoveRacketToPos(Vector3 mousePosition)
	{
		Vector3 vector = ToWorldPositionFromScreenPoint(mousePosition);
		if (!(vector == Vector3.zero))
		{
			LinkedList<FingerPos> queue = gestureTracker.GetQueue();
			if (queue.Last != null && queue.Last.Value.deltaTime > 4f * Time.deltaTime)
			{
				queue.Clear();
			}
			gestureTracker.AddPos(new FingerPos
			{
				pos = vector,
				screenPos = mousePosition,
				deltaTime = Time.deltaTime,
				realTime = time
			});
			vector.x = Mathf.Min(Mathf.Abs(vector.x), Table.halfwidth * 2f) * Mathf.Sign(vector.x);
			racket.position = vector;
			PhysicsBall physicsBall = base.match.ball;
			float num = physicsBall.position.x - racket.position.x;
			racket.rotation = Quaternion.Lerp(racket.rotation, Quaternion.AngleAxis((float)MathEx.SignZeroPositive(num) * Mathf.Lerp(0f, 1f, 4f * Mathf.Abs(num) / Table.halfwidth) * -70f, Vector3.forward), Time.deltaTime * 20f);
		}
	}

	public Vector3 CalculateVelocityFromGestureTracker()
	{
		if (autoPilot)
		{
			Vector3 vector = new Vector3(0f, ball.position.y, (float)(-base.tableSide) * Table.halfLength * 0.5f) - ball.position;
			RacketSettings.Racket currentRacket = base.match.ui.racketButton.currentRacket;
			float max = currentRacket.fingerSpeedRange.max;
			if (currentRacket.type == RacketSettings.SettingsType.RangeSettings)
			{
				max = currentRacket.ranges[currentRacket.ranges.Count - 1].fingerSpeedRange.max;
			}
			max *= Random.Range(autoSpeed.min, autoSpeed.max);
			return vector.normalized * max;
		}
		if (gestureTracker.Count() < 2)
		{
			base.match.ui.debugLabels[0].text = "Speed: " + 0;
			base.match.ui.debugLabels[1].text = "Count: " + gestureTracker.Count();
			return Vector3.zero;
		}
		LinkedList<FingerPos> queue = gestureTracker.GetQueue();
		FingerPos value = queue.First.Value;
		Vector3 vector2 = ToWorldPositionFromScreenPoint(queue.Last.Value.screenPos);
		Vector3 vector3 = ToWorldPositionFromScreenPoint(value.screenPos);
		LinkedListNode<FingerPos> linkedListNode = queue.Last;
		Vector3 vector4 = Vector3.zero;
		int num = 0;
		int num2 = 2;
		while (linkedListNode.Previous != null)
		{
			LinkedListNode<FingerPos> previous = linkedListNode.Previous;
			Vector3 vector5 = ToWorldPositionFromScreenPoint(linkedListNode.Value.screenPos);
			Vector3 vector6 = ToWorldPositionFromScreenPoint(previous.Value.screenPos);
			Vector3 vector7 = vector5 - vector6;
			Vector3 normalized = vector7.normalized;
			vector4 += vector7 / linkedListNode.Value.deltaTime;
			num++;
			if (Mathf.Abs(normalized.z) > 0.2f && Mathf.Sign(normalized.z) == (float)base.tableSide && num > num2)
			{
				break;
			}
			linkedListNode = previous;
		}
		vector4 /= (float)num;
		if (Application.platform == RuntimePlatform.OSXEditor)
		{
			vector4 = vector4.normalized * vector4.magnitude / 1.7f;
		}
		base.match.ui.debugLabels[0].text = string.Concat("Speed: ", vector4, " m: ", vector4.magnitude / Table.length);
		base.match.ui.debugLabels[1].text = "Count: " + num;
		return vector4;
	}

	private Vector3 ToWorldPositionFromScreenPoint(Vector3 screenPoint)
	{
		Camera component = cam.GetComponent<Camera>();
		Ray ray = component.ScreenPointToRay(screenPoint);
		float enter = 0f;
		if (!movePlane.Raycast(ray, out enter))
		{
			return Vector3.zero;
		}
		Vector3 point = ray.GetPoint(enter);
		float num = 0.1f * Table.halfLength;
		if (Mathf.Sign(point.z) != (float)base.tableSide || Mathf.Abs(point.z) < num)
		{
			point.z = num * (float)base.tableSide;
		}
		return point;
	}

	public void HitBall()
	{
		if (ball == null)
		{
			Debug.LogError("ball=null");
			return;
		}
		RacketSettings.Racket racket = racketDef;
		PhysicsBall physicsBall = ball;
		if (IsInServe())
		{
			Vector3 position = ball.position;
			position.y = Mathf.Max(this.racket.position.y, position.y);
			ball.position = position;
		}
		racket.HitBall(this, ball);
		serveHelper.Reset();
		base.match.ui.ballMarker.Hide();
		fire.gameObject.SetActive(false);
		gestureTracker.Clear();
		TableTennisMultiplayerMatchController tableTennisMultiplayerMatchController = base.match as TableTennisMultiplayerMatchController;
		if (tableTennisMultiplayerMatchController == null)
		{
			float timeToStage = physicsBall.flightSequence.GetTimeToStage(physicsBall.flightSequence.LastStage());
			float gameTimescale = timeToStage / (timeToStage + pingTime * 0.001f / Time.timeScale);
			GGGameTime.gameTimescale = gameTimescale;
		}
		hitBall = false;
	}

	public void HitBall(Vector3 landingPos, float heightAboveNet, float timeToLand, float spinx, float spiny, bool isServe, bool useNormalTime = true, float airResistance = 0f, float rebound = 1f, float racketTimeScale = 1f)
	{
		if (isServe || ball.flightSequence.isActive || ball.isBallInGame)
		{
			HitParameters hitParams = default(HitParameters);
			if (isServe)
			{
				AnalyticFlightTrajectory.SequenceSimulationParams sim = AnalyticFlightTrajectory.SequenceSimulationParams.DefaultRecordFlightParams(serveFlight, ball, landingPos, heightAboveNet, timeToLand, spinx, 0f);
				sim.airResistance = airResistance;
				sim.rebound = rebound;
				AnalyticFlightTrajectory instance = Singleton<AnalyticFlightTrajectory>.instance;
				instance.HitServe(ball.flightSequence, sim);
				ball.flightSequence.Reset();
				serveHelper.Reset();
			}
			else
			{
				AnalyticFlightTrajectory.SequenceSimulationParams sim2 = AnalyticFlightTrajectory.SequenceSimulationParams.DefaultRecordFlightParams(serveFlight, ball, landingPos, heightAboveNet, timeToLand, spinx, spiny);
				sim2.useNormalTime = useNormalTime;
				sim2.airResistance = airResistance;
				sim2.rebound = rebound;
				AnalyticFlightTrajectory instance2 = Singleton<AnalyticFlightTrajectory>.instance;
				instance2.HitBall(ball.flightSequence, sim2);
				ball.flightSequence.Reset();
			}
			HitBallParams hitBallParams = default(HitBallParams);
			hitBallParams.ballPosition = ball.position;
			hitBallParams.landingPosition = landingPos;
			hitBallParams.heightAboveNet = heightAboveNet;
			hitBallParams.timeToLand = timeToLand;
			hitBallParams.spinx = spinx;
			hitBallParams.spiny = spiny;
			hitBallParams.isServe = isServe;
			hitBallParams.useNormalTime = useNormalTime;
			hitBallParams.airResistance = airResistance;
			hitBallParams.rebound = rebound;
			hitBallParams.racketTimeScale = racketTimeScale;
			ball.ResetTimescale();
			ball.racketTimeScale = racketTimeScale;
			ball.hitParams = hitParams;
			ball.isServeBall = isServe;
			ball.playerThatHitBall = this;
			ball = null;
			base.match.OnPlayerHitBall(this, hitBallParams);
			serveHelper.isInServe = false;
			base.match.ui.ballFollower.fire.SetActive(false);
		}
	}

	public override void BallRemovedFromGame()
	{
		ball = null;
		serveHelper.Reset();
		base.match.ui.ballMarker.Hide();
	}

	public override void StartServe(PhysicsBall ball)
	{
		GGDebug.DebugLog("Start Serve");
		this.ball = ball;
		this.ball.SetKinematic(true);
		hitBall = false;
		base.match.ui.qualityDisplay.SetRanges(0f, 0f, 1f, false);
		base.match.ui.arrow.gameObject.SetActive(false);
		serveHelper.isInServe = true;
		maxDistanceBehindBallInServe = 0f;
		base.match.ui.ballMarker.Hide();
		Vector3 position = racket.position;
		position.y = Table.tabletopy;
		position.x = Mathf.Clamp(position.x, 0f - Table.halfwidth, Table.halfwidth);
		ball.position = position;
		FlightParams flightParams = default(FlightParams);
		flightParams.position = position;
		flightParams.velocity = ParabolicFlight.initialVelocityForSimpleCase(position, position, Table.tabletopy + 0.3f);
		flightParams.angularVelocity = Vector3.zero;
		FlightParams initialConditions = flightParams;
		serveFlight.gravity = Physics.gravity;
		serveFlight.SetInitialConditions(initialConditions, null);
		serveFlight.Reset();
		base.match.ui.ballFollower.fire.SetActive(false);
		if (autoPilot)
		{
			StopAllCoroutines();
			StartCoroutine(MoveToPos(new Vector3(0f, racket.position.y, (float)base.tableSide * Table.halfLength * 1.3f), 30f));
			this.WaitAndExecute(0.5f, HitBall);
		}
	}

	public override void AwaitServe()
	{
		ball = null;
		base.match.ui.ballMarker.Hide();
		base.match.ui.ballFollower.fire.SetActive(false);
		hitBall = false;
	}

	public override void ReceiveBall(PhysicsBall ball)
	{
		this.ball = ball;
		this.ball.SetKinematic(true);
		hitBall = false;
		FlightSequence.FlightPlanStage stageThatStartsFromTableSide = ball.flightSequence.GetStageThatStartsFromTableSide(base.tableSide);
		if (stageThatStartsFromTableSide != null && stageThatStartsFromTableSide.flight != null)
		{
			Vector3 vector = stageThatStartsFromTableSide.flight.PositionAt(stageThatStartsFromTableSide.flight.apexTime);
			highestPositionAfterRebound = vector;
			float timeToStage = ball.flightSequence.GetTimeToStage(stageThatStartsFromTableSide);
			base.match.ui.ballMarker.Show(ball, stageThatStartsFromTableSide);
		}
		if (autoPilot && Table.IsPosOnTable(ball.flightSequence.landingPos))
		{
			FlightSequence.FlightPlanStage flightPlanStage = ball.flightSequence.LastStage();
			float flightDuration = ball.flightSequence.flightDuration;
			Vector3 vector2 = flightPlanStage.flight.PositionAt(flightPlanStage.flight.apexTime);
			Vector3 endPos = new Vector3(vector2.x, racket.position.y, vector2.z);
			float num = flightDuration + flightPlanStage.flight.apexTime;
			StopAllCoroutines();
			StartCoroutine(MoveToPos(endPos, 40f));
		}
	}

	private IEnumerator MoveToPos(Vector3 endPos, float speed)
	{
		Vector3 startPos = racket.position;
		float duration = Vector3.Distance(endPos, startPos) / speed;
		float time = 0f;
		while (time < duration)
		{
			time += Time.deltaTime;
			racket.position = Vector3.Lerp(startPos, endPos, time / duration);
			yield return null;
		}
	}
}
